#include "rrcuboid.h"
#include "rrtracer.h"
#include <cmath>

RRCuboid::RRCuboid(RRTracer *p)
{
    parent = p;
    type = RR_CUBOID;
}


float RRCuboid::rrHitSurface(RRRay ray, RRSURFACE dir, float dmin)
{
    RRVec3 d = ray.rrGetDir();
    RRVec3 r = ray.rrGetSrc();
    RRVec3 N;
    RRVec3 P[4];
    float denomi, numera, dis_temp, t;
    RRVec3 tosrc_temp, C, cross_temp;
    //頂点, 法線の初期化
    if(dir == RR_UPPER) {
        N = RRSetVec3(0.0f, -1.0f, 0.0f);
        P[0] = RRSetVec3(coord.x - size.x / 2.0f, coord.y - size.y / 2.0f, coord.z - size.z / 2.0f);
        P[1] = RRSetVec3(coord.x + size.x / 2.0f, coord.y - size.y / 2.0f, coord.z - size.z / 2.0f);
        P[2] = RRSetVec3(coord.x + size.x / 2.0f, coord.y - size.y / 2.0f, coord.z + size.z / 2.0f);
        P[3] = RRSetVec3(coord.x - size.x / 2.0f, coord.y - size.y / 2.0f, coord.z + size.z / 2.0f);
    }
    else if(dir == RR_DOWN) {
        N = RRSetVec3(0.0f, 1.0f, 0.0f);
        P[0] = RRSetVec3(coord.x - size.x / 2.0f, coord.y + size.y / 2.0f, coord.z - size.z / 2.0f);
        P[1] = RRSetVec3(coord.x - size.x / 2.0f, coord.y + size.y / 2.0f, coord.z + size.z / 2.0f);
        P[2] = RRSetVec3(coord.x + size.x / 2.0f, coord.y + size.y / 2.0f, coord.z + size.z / 2.0f);
        P[3] = RRSetVec3(coord.x + size.x / 2.0f, coord.y + size.y / 2.0f, coord.z - size.z / 2.0f);
    }
    else if(dir == RR_FRONT) {
        N = RRSetVec3(0.0f, 0.0f, -1.0f);
        P[0] = RRSetVec3(coord.x - size.x / 2.0f, coord.y - size.y / 2.0f, coord.z - size.z / 2.0f);
        P[1] = RRSetVec3(coord.x - size.x / 2.0f, coord.y + size.y / 2.0f, coord.z - size.z / 2.0f);
        P[2] = RRSetVec3(coord.x + size.x / 2.0f, coord.y + size.y / 2.0f, coord.z - size.z / 2.0f);
        P[3] = RRSetVec3(coord.x + size.x / 2.0f, coord.y - size.y / 2.0f, coord.z - size.z / 2.0f);
    }
    else if(dir == RR_REAR) {
        N = RRSetVec3(0.0f, 0.0f, 1.0f);
        P[0] = RRSetVec3(coord.x + size.x / 2.0f, coord.y - size.y / 2.0f, coord.z + size.z / 2.0f);
        P[1] = RRSetVec3(coord.x + size.x / 2.0f, coord.y + size.y / 2.0f, coord.z + size.z / 2.0f);
        P[2] = RRSetVec3(coord.x - size.x / 2.0f, coord.y + size.y / 2.0f, coord.z + size.z / 2.0f);
        P[3] = RRSetVec3(coord.x - size.x / 2.0f, coord.y - size.y / 2.0f, coord.z + size.z / 2.0f);
    }
    else if(dir == RR_RIGHT) {
        N = RRSetVec3(1.0f, 0.0f, 0.0f);
        P[0] = RRSetVec3(coord.x + size.x / 2.0f, coord.y - size.y / 2.0f, coord.z - size.z / 2.0f);
        P[1] = RRSetVec3(coord.x + size.x / 2.0f, coord.y + size.y / 2.0f, coord.z - size.z / 2.0f);
        P[2] = RRSetVec3(coord.x + size.x / 2.0f, coord.y + size.y / 2.0f, coord.z + size.z / 2.0f);
        P[3] = RRSetVec3(coord.x + size.x / 2.0f, coord.y - size.y / 2.0f, coord.z + size.z / 2.0f);
    }
    else if(dir == RR_LEFT) {
        N = RRSetVec3(-1.0f, 0.0f, 0.0f);
        P[0] = RRSetVec3(coord.x - size.x / 2.0f, coord.y - size.y / 2.0f, coord.z + size.z / 2.0f);
        P[1] = RRSetVec3(coord.x - size.x / 2.0f, coord.y + size.y / 2.0f, coord.z + size.z / 2.0f);
        P[2] = RRSetVec3(coord.x - size.x / 2.0f, coord.y + size.y / 2.0f, coord.z - size.z / 2.0f);
        P[3] = RRSetVec3(coord.x - size.x / 2.0f, coord.y - size.y / 2.0f, coord.z - size.z / 2.0f);
    }
    denomi = N.x * d.x + N.y * d.y + N.z * d.z;
    //交点の計算
    if(denomi == 0) return -1.0f;
    else {
        numera = N.x * (P[0].x - r.x) + N.y * (P[0].y - r.y) + N.z * (P[0].z - r.z);
        t = numera / denomi;
        if(t <= 1.0e-4) return -1.0f;
        else {
            C = RRSetVec3(r.x + t * d.x, r.y + t * d.y, r.z + t * d.z);
            tosrc_temp = RRSetVec3(-t * d.x, -t * d.y, -t * d.z);
            tosrc_temp = RRNormalize(tosrc_temp);
            dis_temp =  RRDistance(r, C);
            if(dis_temp > dmin) return -1.0f;
        }
    }
    //ポリゴン内かのテスト
    for(int i=0; i<4; i++) {
        RRVec3 PC, PP, NN;
        float diff_x, diff_y, diff_z;
        int j;
        if(4-i == 4) j = 0; else j = 4-i;
        PC = RRSetVec3(C.x - P[j].x, C.y - P[j].y, C.z - P[j].z);
        PP = RRSetVec3(P[3-i].x - P[j].x, P[3-i].y - P[j].y, P[3-i].z - P[j].z);
        NN = RRCrossProduct(PC, PP); NN = RRNormalize(NN);
        diff_x = fabsf(NN.x - N.x);
        diff_y = fabsf(NN.y - N.y);
        diff_z = fabsf(NN.z - N.z);
        if(diff_x > 1.0e-3 || diff_y > 1.0e-3 || diff_z > 1.0e-3) {
            return -1.0f;
        }
    }
    Norm = N;
    tosrc = tosrc_temp;
    cross = C;
    return dis_temp;
}

float RRCuboid::rrHit(RRRay arg)
{
    float dmin = 1.0e+18;
    float d;
    d = rrHitSurface(arg, RR_UPPER, dmin);
    if(d > 0.0f && d < dmin) dmin = d;
    d = rrHitSurface(arg, RR_DOWN, dmin);
    if(d > 0.0f && d < dmin) dmin = d;
    d = rrHitSurface(arg, RR_FRONT, dmin);
    if(d > 0.0f && d < dmin) dmin = d;
    d = rrHitSurface(arg, RR_REAR, dmin);
    if(d > 0.0f && d < dmin) dmin = d;
    d = rrHitSurface(arg, RR_RIGHT, dmin);
    if(d > 0.0f && d < dmin) dmin = d;
    d = rrHitSurface(arg, RR_LEFT, dmin);
    if(d > 0.0f && d < dmin) dmin = d;

    if(dmin < 1.0e+18)
        return dmin;
    else
        return -1.0f;
}

RRVec4 RRCuboid::rrCalcShadow(RRRay arg)
{
    RRLight l;
    l = parent->rrGetLight();
    RRVec3 l_c = l.rrGetCoord();
    float d_l;
    d_l = RRDistance(cross, l_c);

    RRPrim *p;
    float d, dmin;
    int it, index;
    dmin = 1.0e+18;
    it = primset->rrBegin();
    index = -1;
    while(it != primset->rrEnd()) {
        p = primset->rrGetPrim(it);
        d = p->rrHit(arg);
        //printf("distance:%f\n", d);
        if(d < dmin && d > 0.0f) {
            dmin = d;
            index = it;
        }
        it++;
    }

    if(index != -1) {
        if(dmin >= d_l)
            return RRSetVec4(1.0f, 1.0f, 1.0f, 1.0f);
        else
            return RRSetVec4(0.0f, 0.0f, 0.0f, 1.0f);
    }
    return RRSetVec4(1.0f, 1.0f, 1.0f, 1.0f);
}

RRVec4 RRCuboid::rrCalcGlare(RRVec3 N)
{
    float D;
    D = 2 * RRInnerProduct(tosrc, N);
    RRVec3 R;
    R = RRSetVec3(D * N.x - tosrc.x, D * N.y - tosrc.y, D * N.z - tosrc.z);
    RRRay r;
    r.rrSetSrc(cross);
    r.rrSetDir(R);

    RRPrim *p;
    float d, dmin;
    int it, index;
    dmin = 1.0e+10;
    it = primset->rrBegin();
    index = -1;
    while(it != primset->rrEnd()) {
        p = primset->rrGetPrim(it);
        d = p->rrHit(r);
        //printf("distance:%f\n", d);
        if(d < dmin && d > 0.0f) {
            dmin = d;
            index = it;
        }
        it++;
    }

    if(index != -1) {
        RRVec4 c;
        p = primset->rrGetPrim(index);
        c = p->rrTrace();
        return c;
    }
    else
        return RRSetVec4(0.0f, 0.0f, 0.0f, -1.0f);
}

RRVec4 RRCuboid::rrTrace()
{
    RRLight l;
    l = parent->rrGetLight();
    RRVec3 l_c = l.rrGetCoord();
    RRVec3 L;
    L = RRSetVec3(l_c.x - cross.x, l_c.y - cross.y, l_c.z - cross.z);
    L = RRNormalize(L);

    RRRay r;
    r.rrSetSrc(cross);
    r.rrSetDir(L);

    RRVec4 sh = RRSetVec4(1.0f, 1.0f, 1.0f, 1.0f);
    sh = rrCalcShadow(r);

    RRVec4 gl;
    if(reflection == true) {
            gl = rrCalcGlare(Norm);
            return gl;
    }

    float D;
    D = RRInnerProduct(Norm, L);
    D = (float)reversal * D;
    amb = RRSetVec4(1.0f, 0.4f, 0.4f, 1.0f);
    if(D < 0.0f) D = 0.0f;
    float w_temp = diff.w + sh.w;
    float r_temp, g_temp, b_temp;
    r_temp = (diff.x * D * diff.w + sh.x * sh.w) / w_temp;
    g_temp = (diff.y * D * diff.w + sh.y * sh.w) / w_temp;
    b_temp = (diff.z * D * diff.w + sh.z * sh.w) / w_temp;
    return RRSetVec4(r_temp, g_temp, b_temp, 1.0f);
}
